package com.itdct.onflow.core.util

import com.itdct.onflow.core.extend.logError
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.stereotype.Component
import java.util.*
import java.util.concurrent.TimeUnit


/**
 * Created by on 2020/9/22.
 * 直接用redisTemplate操作redis，需要很多行代码，因此直接封装好一个redisutils，这样写代码更方便点。这个redisUtils交给spring容器示例化，使用时直接注解注入。
 */
/**
 * @component （把普通pojo示例化到spring容器中，相当于配置文件中的）
 * 泛指各种组件，就是说当我们的类不属于各种归类的时候（不属于@Controller、@Services等的时候），我们就可以使用@Component来标注这个类。
 * 1、@Service用于标注业务层组件
 * 2、@Controller用于标注控制层组件(如struts中的action)
 * 3、@Repository用于标注数据访问组件，即DAO组件.
 * 4、@Component泛指组件，当组件不好归类的时候，我们可以使用这个注解进行标注。
 */
@Component
@ConditionalOnProperty(prefix = "spring.data.redis", name = ["host"])
class RedisUtil(
    val redisTemplate: RedisTemplate<String, Any>
) {
    
    /**
     * 指定缓存失效时间
     * @param key 键
     * @param time 时间（秒）
     * @return true / false
     */
    fun expire(key: String, time: Long): Boolean {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS)
            }
            return true
        } catch (e: Exception) {
            logError(e)
            return false
        }
    }

    /**
     * 根据 key 获取过期时间
     * @param key 键
     * @return
     */
    fun getExpire(key: String): Long? {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS)
    }

    /**
     * 判断 key 是否存在
     * @param key 键
     * @return true / false
     */
    fun hasKey(key: String): Boolean {
        try {
            return redisTemplate.hasKey(key)
        } catch (e: Exception) {
            logError(e)
            return false
        }
    }

    /**
     * 删除缓存
     * @SuppressWarnings("unchecked") 忽略类型转换警告
     * @param key 键（一个或者多个）
     */
    fun del(vararg key: String?) {
        if (key.size > 0) {
            if (key.size == 1) {
                redisTemplate.delete(key[0]!!)
            } else {
//                传入一个 Collection<String> 集合
                redisTemplate.delete(Arrays.asList<String?>(*key))
            }
        }
    }

    //    ============================== String ==============================
    /**
     * 普通缓存获取
     * @param key 键
     * @return 值
     */
    fun get(key: String): Any? {
        return redisTemplate.opsForValue().get(key)
    }

    /**
     * 普通缓存放入
     * @param key 键
     * @param value 值
     * @return true / false
     */
    fun set(key: String, value: Any): Boolean {
        try {
            redisTemplate.opsForValue().set(key, value)
            return true
        } catch (e: Exception) {
            logError(e)
            return false
        }
    }

    /**
     * 普通缓存放入并设置时间
     * @param key 键
     * @param value 值
     * @param time 时间（秒），如果 time < 0 则设置无限时间
     * @return true / false
     */
    fun set(key: String, value: Any, time: Long): Boolean {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS)
            } else {
                set(key, value)
            }
            return true
        } catch (e: Exception) {
            logError(e)
            return false
        }
    }

    /**
     * 递增
     * @param key 键
     * @param delta 递增大小
     * @return
     */
    fun incr(key: String, delta: Long): Long {
        return redisTemplate.opsForValue().increment(key, delta)!!
    }

    /**
     * 递减
     * @param key 键
     * @param delta 递减大小
     * @return
     */
    fun decr(key: String, delta: Long): Long? {
        if (delta < 0) {
            throw RuntimeException("递减因子必须大于 0")
        }
        return redisTemplate.opsForValue().decrement(key, delta)
    }

    //    ============================== Map ==============================
    /**
     * HashGet
     * @param key 键（no null）
     * @param item 项（no null）
     * @return 值
     */
    fun hget(key: String, item: String): Any? {
        return redisTemplate.opsForHash<Any?, Any?>().get(key, item)
    }

    /**
     * 获取 key 对应的 map
     * @param key 键（no null）
     * @return 对应的多个键值
     */
    fun hmget(key: String): MutableMap<Any, Any>? {
        return redisTemplate.opsForHash<Any, Any>().entries(key)
    }

    /**
     * HashSet
     * @param key 键
     * @param map 值
     * @return true / false
     */
    fun hmset(key: String, map: MutableMap<Any, Any>): Boolean {
        try {
            redisTemplate.opsForHash<Any, Any>().putAll(key, map)
            return true
        } catch (e: Exception) {
            logError(e)
            return false
        }
    }

    /**
     * HashSet 并设置时间
     * @param key 键
     * @param map 值
     * @param time 时间
     * @return true / false
     */
    fun hmset(key: String, map: MutableMap<Any, Any>, time: Long): Boolean {
        try {
            redisTemplate.opsForHash<Any, Any>().putAll(key, map)
            if (time > 0) {
                expire(key, time)
            }
            return true
        } catch (e: Exception) {
            logError(e)
            return false
        }
    }

    /**
     * 向一张 Hash表 中放入数据，如不存在则创建
     * @param key 键
     * @param item 项
     * @param value 值
     * @return true / false
     */
    fun hset(key: String, item: String, value: Any): Boolean {
        try {
            redisTemplate.opsForHash<Any, Any>().put(key, item, value)
            return true
        } catch (e: Exception) {
            logError(e)
            return false
        }
    }

    /**
     * 向一张 Hash表 中放入数据，并设置时间，如不存在则创建
     * @param key 键
     * @param item 项
     * @param value 值
     * @param time 时间（如果原来的 Hash表 设置了时间，这里会覆盖）
     * @return true / false
     */
    fun hset(key: String, item: String, value: Any, time: Long): Boolean {
        try {
            redisTemplate.opsForHash<Any, Any>().put(key, item, value)
            if (time > 0) {
                expire(key, time)
            }
            return true
        } catch (e: Exception) {
            logError(e)
            return false
        }
    }

    /**
     * 删除 Hash表 中的值
     * @param key 键
     * @param item 项（可以多个，no null）
     */
    fun hdel(key: String, vararg item: Any?) {
        redisTemplate.opsForHash<Any, Any>().delete(key, *item)
    }

    /**
     * 判断 Hash表 中是否有该键的值
     * @param key 键（no null）
     * @param item 值（no null）
     * @return true / false
     */
    fun hHasKey(key: String, item: String): Boolean {
        return redisTemplate.opsForHash<Any, Any>().hasKey(key, item)
    }

    /**
     * Hash递增，如果不存在则创建一个，并把新增的值返回
     * @param key 键
     * @param item 项
     * @param by 递增大小 > 0
     * @return
     */
    fun hincr(key: String, item: String, by: Double): Double {
        return redisTemplate.opsForHash<Any, Any>().increment(key, item, by)
    }

    /**
     * Hash递减
     * @param key 键
     * @param item 项
     * @param by 递减大小
     * @return
     */
    fun hdecr(key: String, item: String, by: Double): Double {
        return redisTemplate.opsForHash<Any, Any>().increment(key, item, -by)
    }

    //    ============================== Set ==============================
    /**
     * 根据 key 获取 set 中的所有值
     * @param key 键
     * @return 值
     */
    fun sGet(key: String): MutableSet<Any>? {
        try {
            return redisTemplate.opsForSet().members(key)
        } catch (e: Exception) {
            logError(e)
            return null
        }
    }

    /**
     * 从键为 key 的 set 中，根据 value 查询是否存在
     * @param key 键
     * @param value 值
     * @return true / false
     */
    fun sHasKey(key: String, value: Any): Boolean {
        try {
            return redisTemplate.opsForSet().isMember(key, value)!!
        } catch (e: Exception) {
            logError(e)
            return false
        }
    }

    /**
     * 将数据放入 set缓存
     * @param key 键值
     * @param values 值（可以多个）
     * @return 成功个数
     */
    fun sSet(key: String, vararg values: Any?): Long {
        try {
            return redisTemplate.opsForSet().add(key, *values)!!
        } catch (e: Exception) {
            logError(e)
            return 0
        }
    }

    /**
     * 将数据放入 set缓存，并设置时间
     * @param key 键
     * @param time 时间
     * @param values 值（可以多个）
     * @return 成功放入个数
     */
    fun sSet(key: String, time: Long, vararg values: Any?): Long {
        try {
            val count: Long = redisTemplate.opsForSet().add(key, *values)!!
            if (time > 0) {
                expire(key, time)
            }
            return count
        } catch (e: Exception) {
            logError(e)
            return 0
        }
    }

    /**
     * 获取 set缓存的长度
     * @param key 键
     * @return 长度
     */
    fun sGetSetSize(key: String): Long {
        try {
            return redisTemplate.opsForSet().size(key)!!
        } catch (e: Exception) {
            logError(e)
            return 0
        }
    }

    /**
     * 移除 set缓存中，值为 value 的
     * @param key 键
     * @param values 值
     * @return 成功移除个数
     */
    fun setRemove(key: String, vararg values: Any?): Long {
        try {
            return redisTemplate.opsForSet().remove(key, *values)!!
        } catch (e: Exception) {
            logError(e)
            return 0
        }
    }

    //    ============================== List ==============================
    /**
     * 获取 list缓存的内容
     * @param key 键
     * @param start 开始
     * @param end 结束（0 到 -1 代表所有值）
     * @return
     */
    fun lGet(key: String, start: Long, end: Long): MutableList<Any?>? {
        try {
            return redisTemplate.opsForList().range(key, start, end)
        } catch (e: Exception) {
            logError(e)
            return null
        }
    }

    /**
     * 获取 list缓存的长度
     * @param key 键
     * @return 长度
     */
    fun lGetListSize(key: String): Long {
        try {
            return redisTemplate.opsForList().size(key)!!
        } catch (e: Exception) {
            logError(e)
            return 0
        }
    }

    /**
     * 根据索引 index 获取键为 key 的 list 中的元素
     * @param key 键
     * @param index 索引
     * 当 index >= 0 时 {0:表头, 1:第二个元素}
     * 当 index < 0 时 {-1:表尾, -2:倒数第二个元素}
     * @return 值
     */
    fun lGetIndex(key: String, index: Long): Any? {
        try {
            return redisTemplate.opsForList().index(key, index)
        } catch (e: Exception) {
            logError(e)
            return null
        }
    }

    /**
     * 将值 value 插入键为 key 的 list 中，如果 list 不存在则创建空 list
     * @param key 键
     * @param value 值
     * @return true / false
     */
    fun lSet(key: String, value: Any): Boolean {
        try {
            redisTemplate.opsForList().rightPush(key, value)
            return true
        } catch (e: Exception) {
            logError(e)
            return false
        }
    }

    /**
     * 将值 value 插入键为 key 的 list 中，并设置时间
     * @param key 键
     * @param value 值
     * @param time 时间
     * @return true / false
     */
    fun lSet(key: String, value: Any, time: Long): Boolean {
        try {
            redisTemplate.opsForList().rightPush(key, value)
            if (time > 0) {
                expire(key, time)
            }
            return true
        } catch (e: Exception) {
            logError(e)
            return false
        }
    }

    /**
     * 将 values 插入键为 key 的 list 中
     * @param key 键
     * @param values 值
     * @return true / false
     */
    fun lSetList(key: String, values: MutableList<Any>): Boolean {
        try {
            redisTemplate.opsForList().rightPushAll(key, values)
            return true
        } catch (e: Exception) {
            logError(e)
            return false
        }
    }

    /**
     * 将 values 插入键为 key 的 list 中，并设置时间
     * @param key 键
     * @param values 值
     * @param time 时间
     * @return true / false
     */
    fun lSetList(key: String, values: MutableList<Any>, time: Long): Boolean {
        try {
            redisTemplate.opsForList().rightPushAll(key, values)
            if (time > 0) {
                expire(key, time)
            }
            return true
        } catch (e: Exception) {
            logError(e)
            return false
        }
    }

    /**
     * 根据索引 index 修改键为 key 的值
     * @param key 键
     * @param index 索引
     * @param value 值
     * @return true / false
     */
    fun lUpdateIndex(key: String, index: Long, value: Any): Boolean {
        try {
            redisTemplate.opsForList().set(key, index, value)
            return true
        } catch (e: Exception) {
            logError(e)
            return false
        }
    }

    /**
     * 在键为 key 的 list 中删除值为 value 的元素
     * @param key 键
     * @param count 如果 count == 0 则删除 list 中所有值为 value 的元素
     * 如果 count > 0 则删除 list 中最左边那个值为 value 的元素
     * 如果 count < 0 则删除 list 中最右边那个值为 value 的元素
     * @param value
     * @return
     */
    fun lRemove(key: String, count: Long, value: Any): Long {
        try {
            return redisTemplate.opsForList().remove(key, count, value)!!
        } catch (e: Exception) {
            logError(e)
            return 0
        }
    }

    fun keys(keyPrefix: String): MutableSet<String?> {
        var keyPrefix = keyPrefix
        if (!keyPrefix.endsWith("*")) {
            keyPrefix += "*"
        }
        return redisTemplate.keys(keyPrefix)
    }
}

